{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ab032fde",
   "metadata": {},
   "source": [
    "# Chapter 5 - Nonlinear Interactions (Python Code)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f0b02209",
   "metadata": {
    "tags": [
     "hide-output"
    ]
   },
   "outputs": [],
   "source": [
    "! pip install --upgrade quantecon_book_networks"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d82d322",
   "metadata": {},
   "source": [
    "We begin with some imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be38b591",
   "metadata": {},
   "outputs": [],
   "source": [
    "import quantecon as qe\n",
    "import quantecon_book_networks\n",
    "import quantecon_book_networks.input_output as qbn_io\n",
    "import quantecon_book_networks.plotting as qbn_plt\n",
    "import quantecon_book_networks.data as qbn_data\n",
    "export_figures = False"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e481b4fb",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import networkx as nx\n",
    "import matplotlib.pyplot as plt\n",
    "from matplotlib import cm\n",
    "quantecon_book_networks.config(\"matplotlib\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f027495",
   "metadata": {},
   "source": [
    "## Financial Networks\n",
    "\n",
    "### Equity-Cross Holdings\n",
    "\n",
    "Here we define a class for modelling a financial network where firms are linked by share cross-holdings,\n",
    "and there are failure costs as described by [Elliott et al. (2014)](https://www.aeaweb.org/articles?id=10.1257/aer.104.10.3115)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "58e02f09",
   "metadata": {},
   "outputs": [],
   "source": [
    "class FinNet:\n",
    "    \n",
    "    def __init__(self, n=100, c=0.72, d=1, θ=0.5, β=1.0, seed=1234):\n",
    "        \n",
    "        self.n, self.c, self.d, self.θ, self.β = n, c, d, θ, β\n",
    "        np.random.seed(seed)\n",
    "        \n",
    "        self.e = np.ones(n)\n",
    "        self.C, self.C_hat = self.generate_primitives()\n",
    "        self.A = self.C_hat @ np.linalg.inv(np.identity(n) - self.C)\n",
    "        self.v_bar = self.A @ self.e\n",
    "        self.t = np.full(n, θ)\n",
    "        \n",
    "    def generate_primitives(self):\n",
    "        \n",
    "        n, c, d = self.n, self.c, self.d\n",
    "        B = np.zeros((n, n))\n",
    "        C = np.zeros_like(B)\n",
    "\n",
    "        for i in range(n):\n",
    "            for j in range(n):\n",
    "                if i != j and np.random.rand() < d/(n-1):\n",
    "                    B[i,j] = 1\n",
    "                \n",
    "        for i in range(n):\n",
    "            for j in range(n):\n",
    "                k = np.sum(B[:,j])\n",
    "                if k > 0:\n",
    "                    C[i,j] = c * B[i,j] / k\n",
    "                \n",
    "        C_hat = np.identity(n) * (1 - c)\n",
    "    \n",
    "        return C, C_hat\n",
    "        \n",
    "    def T(self, v):\n",
    "        Tv = self.A @ (self.e - self.β * np.where(v < self.t, 1, 0))\n",
    "        return Tv\n",
    "    \n",
    "    def compute_equilibrium(self):\n",
    "        i = 0\n",
    "        v = self.v_bar\n",
    "        error = 1\n",
    "        while error > 1e-10:\n",
    "            print(f\"number of failing firms is \", np.sum(v < self.θ))\n",
    "            new_v = self.T(v)\n",
    "            error = np.max(np.abs(new_v - v))\n",
    "            v = new_v\n",
    "            i = i+1\n",
    "            \n",
    "        print(f\"Terminated after {i} iterations\")\n",
    "        return v\n",
    "    \n",
    "    def map_values_to_colors(self, v, j):\n",
    "        cols = cm.plasma(qbn_io.to_zero_one(v))\n",
    "        if j != 0:\n",
    "            for i in range(len(v)):\n",
    "                if v[i] < self.t[i]:\n",
    "                    cols[i] = 0.0\n",
    "        return cols"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dbf9b681",
   "metadata": {},
   "source": [
    "Now we create a financial network."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "663e7992",
   "metadata": {},
   "outputs": [],
   "source": [
    "fn = FinNet(n=100, c=0.72, d=1, θ=0.3, β=1.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c89db6a0",
   "metadata": {},
   "source": [
    "And compute its equilibrium."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81efad24",
   "metadata": {},
   "outputs": [],
   "source": [
    "fn.compute_equilibrium()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "25a95eb9",
   "metadata": {},
   "source": [
    "### Waves of bankruptcies in a financial network\n",
    "\n",
    "Now we visualise the network after different numbers of iterations. \n",
    "\n",
    "For convenience we will first define a function to plot the graphs of the financial network."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff6c8d54",
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_fin_graph(G, ax, node_color_list):\n",
    "    \n",
    "    n = G.number_of_nodes()\n",
    "\n",
    "    node_pos_dict = nx.spring_layout(G, k=1.1)\n",
    "    edge_colors = []\n",
    "\n",
    "    for i in range(n):\n",
    "        for j in range(n):\n",
    "            edge_colors.append(node_color_list[i])\n",
    "\n",
    "    \n",
    "    nx.draw_networkx_nodes(G, \n",
    "                           node_pos_dict, \n",
    "                           node_color=node_color_list, \n",
    "                           edgecolors='grey', \n",
    "                           node_size=100,\n",
    "                           linewidths=2, \n",
    "                           alpha=0.8, \n",
    "                           ax=ax)\n",
    "\n",
    "    nx.draw_networkx_edges(G, \n",
    "                           node_pos_dict, \n",
    "                           edge_color=edge_colors, \n",
    "                           alpha=0.4,  \n",
    "                           ax=ax)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd11cd19",
   "metadata": {},
   "source": [
    "Now we will iterate by applying the operator $T$ to the vector of firm values $v$ and produce the plots."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c4e15ff6",
   "metadata": {},
   "outputs": [],
   "source": [
    "G = nx.from_numpy_array(np.array(fn.C), create_using=nx.DiGraph)\n",
    "v = fn.v_bar\n",
    "\n",
    "k = 15\n",
    "d = 3\n",
    "fig, axes = plt.subplots(int(k/d), 1, figsize=(10, 12))\n",
    "\n",
    "for i in range(k):\n",
    "    if i % d == 0:\n",
    "        ax = axes[int(i/d)]\n",
    "        ax.set_title(f\"iteration {i}\")\n",
    "\n",
    "        plot_fin_graph(G, ax, fn.map_values_to_colors(v, i))\n",
    "    v = fn.T(v)\n",
    "if export_figures:\n",
    "    plt.savefig(\"figures/fin_network_sims_1.pdf\")\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "jupytext": {
   "cell_metadata_filter": "-all"
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
